package org.sdp.pearproject.config;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.sdp.pearproject.dto.request.BaseRequest;
import org.sdp.pearproject.utils.JavaBeansUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.ExtendedServletRequestDataBinder;

import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;

@Aspect
@Component
@Order(-1)
@Slf4j
public class GlobalAop {

    @Autowired
    HttpServletResponse httpServletResponse;

    @Autowired
    HttpServletRequest httpServletRequest;

    @Autowired
    ThreadLocalResponse threadLocalResponse;

    @Pointcut("execution(public * org.sdp.pearproject.controller..*.*(..))")
    public void accessPointcut() {
    }

    @Around("accessPointcut()")
    public Object around(ProceedingJoinPoint pjd) throws Throwable {
        doBeforeProcess(pjd);
        return pjd.proceed();
    }

    @AfterReturning(pointcut = "accessPointcut()", returning = "returnValue")
    public void afterReturning(Object returnValue) {

        if (!Integer.valueOf(1).equals(threadLocalResponse.getInteger("is_pear"))) {
            return;
        }

        String json;
        String Authorization = httpServletRequest.getHeader("Authorization");

        String organizationCode = httpServletRequest.getHeader("organizationCode");

        JSONObject jsonObject = new JSONObject();

        if (StringUtils.hasText(organizationCode)
                || threadLocalResponse.get("code") != null
                || threadLocalResponse.get("msg") != null
        ) {
            String message = threadLocalResponse.getString("message");
            if (!StringUtils.hasText(message)) {
                message = "";
            }
            Integer code = threadLocalResponse.getInteger("code");
            if (code == null) {
                code = 200;
            }
            jsonObject.put("msg", message);
            jsonObject.put("code", code);
            if (returnValue instanceof List && !Integer.valueOf(1).equals(threadLocalResponse.getInteger("without_total"))) {
                jsonObject.put("data", new JSONObject());

                jsonObject.getJSONObject("data").put("list", returnValue);
                Integer total = threadLocalResponse.getInteger("total");
                if (total == null) {
                    total = ((List)returnValue).size();
                }
                jsonObject.getJSONObject("data").put("total", total);
                Integer page = threadLocalResponse.getInteger("page");
                if (page == null) {
                    page = 1;
                }
                jsonObject.getJSONObject("data").put("page", page);
            } else {
                jsonObject.put("data", returnValue);
            }
        } else {
            jsonObject.put("code", 0);
            jsonObject.put("msg", "");
            jsonObject.put("body", returnValue);
        }
        json = JSON.toJSONString(jsonObject, SerializerFeature.DisableCircularReferenceDetect);
        if (json.length() < 200) {
            log.info("response {}: {}", json.length(), json);
        } else {
            log.info("response truncate {}: {}", json.length(), json.substring(0, 200));
        }

        threadLocalResponse.set("is_pear", null);
        threadLocalResponse.set("code", null);
        threadLocalResponse.set("message", null);
        threadLocalResponse.set("page", null);
        threadLocalResponse.set("without_total", null);

        ServletOutputStream out = null;
        try {
            httpServletResponse.setCharacterEncoding("UTF-8");
            httpServletResponse.setContentType("application/json;charset=UTF-8");
            out = httpServletResponse.getOutputStream();
            out.write(json.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != out) {
                try {
                    out.flush();
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public void doBeforeProcess(JoinPoint joinPoint) throws Exception {
        Assert.notNull(httpServletRequest, "Request must not be null");

        String url = httpServletRequest.getRequestURL().toString();

        threadLocalResponse.setResponseInfo(new JSONObject());

        List<Object> argsForLog = new ArrayList<>();

        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            if (args[i] instanceof ServletRequest
                    || args[i] instanceof ServletResponse
                    || args[i] instanceof MultipartFile
                    || args[i] instanceof ExtendedServletRequestDataBinder) {
                continue;
            }
            if (args[i] instanceof BaseRequest) {

                BaseRequest baseRequest = (BaseRequest) args[i];

                if (baseRequest.getPage() != null) {
                    baseRequest.setPageNo(baseRequest.getPage());
                }
                try {
                    Field[] fields = baseRequest.getClass().getDeclaredFields();
                    for(Field field: fields) {
                        try {
                            if (List.class.equals(field.getType())) {
                                if ("java.util.List<java.lang.String>".equals(field.getGenericType().getTypeName())) {
                                    field.setAccessible(true);
                                    Object v = field.get(args[i]);
                                    if (v != null) {
                                        field.set(args[i], JavaBeansUtil.fixStringArray((List<String>) v));
                                    }
                                }
                            }
                        }catch (Exception ex) {

                        }
                    }
                }catch(Exception ex) {

                }
            }
            argsForLog.add(args[i]);
        }
        log.info("request url: {}, param: {}", url, JSON.toJSONString(argsForLog, SerializerFeature.DisableCircularReferenceDetect));
    }
}
